%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Industrial Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% createMissingDirs.m
% getFileSequenceFromRepresentative.m
% getImagesFromMovie.m
% getImagesFromImages.m
% mergeRangeIntoSeparatedRanges.m
% ffmpeg
%
%==========================================================================

%==========================================================================
% The function "secureReadableImages" takes a project structure already
% populated with the information from an input movie file or a sequence of
% input image files, and extracts a specified range of frames from the
% movie file or converts a specified range of the image file to
% Matlab-readable image files written on a disk. This is done prudently so
% that if any of those frames were previously extracted or converted to
% properly stored image files by this or another project, then those image
% files are reused and not created again.
% INPUT: "projectStruct" is a project structure whose fields store various
% information about the input movie file relevant to optical sound
% extraction.
% "newFrameRange" is a row vector of length two whose entries specify the
% indices of the first and the last frame which need to be written to image
% files. Indexing starts with 1.
% "imageFileExt" is an optional string specifying a format for the output
% image files where the requested frames are to be written.
% OUTPUT: "projectStruct" returns a project structure whose fields store
% various information about the input movie file relevant to optical sound
% extraction.
%==========================================================================
function projectStruct = secureReadableImages(projectStruct, newFrameRange, imageFileExt)

if (nargin < 3)
    if ( (isfield(projectStruct, 'workFile') == true)&& ...
            (isfield(projectStruct.workFile, 'fileExt') == true)&& ...
            (isempty(projectStruct.workFile.fileExt) == false) )
        % If an output image format is not specified in the call to this
        % function, then use the one specified in the project structure, if
        % available.
        imageFileExt = projectStruct.workFile.fileExt;
    else
        % If an output image format is specified in neither the call to
        % this function, nor the project structure, then use .pgm.
        imageFileExt = 'pgm';
    end
end
% In any case, save the output image format to the project (overwriting
% anything that might have been there).
projectStruct.workFile.fileExt = imageFileExt;

% Make sure that function is called only on a movie that is not
% Matlab-readable.
if ( (strcmpi(projectStruct.inFile.kind, 'videoNonMatlab') == false)&& ...
        (strcmpi(projectStruct.inFile.kind, 'imageNonMatlab') == false) )
    warning('SoundRestoration:secureReadableImages:InappropriateInputFile', ...
        '(in secureReadableImages) Inappropriate input file %s.\n', projectStruct.inFile.name);
    return;
end

% If the project's field corresponding to the working image files and some
% of its subfields do not exist, then it is impossible to proceed.
if ( (isfield(projectStruct, 'workFile') == false)||...
        (isfield(projectStruct.workFile, 'namePrefix') == false)||...
        (isfield(projectStruct.workFile, 'indFieldLength') == false) )
    return;
end

% If no frame has been already stored in an image file, then create and
% initialize the field that keeps track of the frames already stored in
% image files.
if ( (isfield(projectStruct.workFile, 'frameRangeArr') == false)||...
        (isempty(projectStruct.workFile.frameRangeArr) == true) )
    
    % Generate a template file name for the image files where frames would
    % normally be stored.
    templateFileName = [projectStruct.workFile.namePrefix, ...
        num2str(projectStruct.inFile.frameRangeArr(1), ...
        ['%0' num2str(projectStruct.workFile.indFieldLength) 'd']), ...
        '.' projectStruct.workFile.fileExt];
    % Check the directory where frames would normally be stored to image
    % files for such files written by other projects.
    [~, ~, ~, ~, projectStruct.workFile.frameRangeArr] = ...
        getFileSequenceFromRepresentative(templateFileName, [0, Inf], true);
end

% Keep a copy of the original requested range.
origNewFrameRange = newFrameRange;
% Correct the requested range, if necessary, to make sure it is contained
% within the available source range.
newFrameRange(1) = max(newFrameRange(1), projectStruct.inFile.frameRangeArr(1));
newFrameRange(2) = min(newFrameRange(2), projectStruct.inFile.frameRangeArr(2));

% Check if the requested range meets the available source at all.
if (newFrameRange(2) < newFrameRange(1))
    warning('SoundRestoration:secureReadableImages:RequestedRangeOutsideSourceRange', ...
        ['(in secureReadableImages) Requested range of [' ...
        num2str(origNewFrameRange(1)) ',' num2str(origNewFrameRange(2)) ...
        '] is completely outside of the available source range of [' ...
        num2str(projectStruct.inFile.frameRangeArr(1)) ',' ...
        num2str(projectStruct.inFile.frameRangeArr(2)) '].\n']);
    return;
elseif ( (newFrameRange(1) > origNewFrameRange(1))||(newFrameRange(2) < origNewFrameRange(2)) )
    warning('SoundRestoration:secureReadableImages:RequestedRangeNotInSourceRange', ...
        ['(in secureReadableImages) Requested range of [' ...
        num2str(origNewFrameRange(1)) ',' num2str(origNewFrameRange(2)) ...
        '] can not be satisfied with the available source range of [' ...
        num2str(projectStruct.inFile.frameRangeArr(1)) ',' ...
        num2str(projectStruct.inFile.frameRangeArr(2)) '], and is reduced to [' ...
        num2str(newFrameRange(1)) ',' num2str(newFrameRange(2)) '].\n']);
end

% Figure out the indices of the frames in the requested range that have not
% been stored in image files yet. Doing this repeatedly requires keeping
% track of the frames already stored in image files so that they do not
% have to be extracted again in subsequent requests.
[projectStruct.workFile.frameRangeArr, newSubRangesArr] = ...
    mergeRangeIntoSeparatedRanges(newFrameRange, projectStruct.workFile.frameRangeArr);

% Generate the still-missing, grayscale, Matlab-readable image files.
if (strcmpi(projectStruct.inFile.kind, 'videoNonMatlab') == true)
    
    getImagesFromMovie(projectStruct.inFile.name, projectStruct.frameRate, ...
        newSubRangesArr, projectStruct.workFile.namePrefix, ...
        projectStruct.workFile.indFieldLength, imageFileExt);
    
elseif (strcmpi(projectStruct.inFile.kind, 'imageNonMatlab') == true)
    
    getImagesFromImages([projectStruct.inFile.dirPath projectStruct.inFile.namePrefixNoPath], ...
        projectStruct.inFile.indFieldLength, projectStruct.inFile.fileExt, ...
        newSubRangesArr, projectStruct.workFile.namePrefix, ...
        projectStruct.workFile.indFieldLength, projectStruct.workFile.fileExt);
    
end

return;
% end of the function "secureReadableImages"
